#include "cn_udp.h"
#include "fifo_process.h"
#include "wm_sockets.h"

#define     DEMO_UDP_TASK_SIZE      512
#define     DEMO_UDP_BUF_SIZE            1024

#define DEMO_UDP_LOCAL_PORT         5000

static OS_STK   udp_task_stk[DEMO_UDP_TASK_SIZE];
static OS_STK   udp_rcv_task_stk[DEMO_UDP_TASK_SIZE];
static socklen_t addrlen = sizeof(struct sockaddr);
static struct sockaddr_in pin;
ST_Udp_Para *cn_udp = NULL;

static void demo_udp_task(void *sdata);

int create_udp_socket_demo(void)
{
    struct sockaddr_in pin;
    struct tls_ethif *ethif;
    int ttl = 10;
    int loop = 0;

    ethif = tls_netif_get_ethif();
    printf("local ip : %d.%d.%d.%d\n",  ip4_addr1(ip_2_ip4(&ethif->ip_addr)), ip4_addr2(ip_2_ip4(&ethif->ip_addr)),

    ip4_addr3(ip_2_ip4(&ethif->ip_addr)), ip4_addr4(ip_2_ip4(&ethif->ip_addr)));

     printf("dns2  ip : %d.%d.%d.%d\n",  ip4_addr1(ip_2_ip4(&ethif->dns2)), ip4_addr2(ip_2_ip4(&ethif->dns2)),

    ip4_addr3(ip_2_ip4(&ethif->dns2)), ip4_addr4(ip_2_ip4(&ethif->dns2)));

    cn_udp->socket_num = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    memset(&pin, 0, sizeof(struct sockaddr_in));
    pin.sin_family = AF_INET;               
    pin.sin_addr.s_addr = (u32)0x00000000UL;        //IPADDR_ANY
    pin.sin_port = htons(DEMO_UDP_LOCAL_PORT);

    printf("local port :%d\n", DEMO_UDP_LOCAL_PORT);
    if (bind(cn_udp->socket_num, (struct sockaddr *) &pin, sizeof(pin)) == -1)
    {
//        printf("bind err\n");
        closesocket(cn_udp->socket_num);
        return WM_FAILED;
    }
    cn_udp->socket_ok = TRUE;
    return WM_SUCCESS;
}

int send_udp(uint8_t *data,uint32_t len)
{
    uint32_t readlen = len;
    uint32_t ret = 0,offset = 0; 
    if (cn_udp->socket_ok)
    {
        sendto(cn_udp->socket_num, data, len, 0, (struct sockaddr *)&pin, sizeof(struct sockaddr));
    }
    return 0; 
}

static void demo_udp_recv_task(void *sdata)
{
    ST_Udp_Para *udp = (ST_Udp_Para *)sdata;
    int ret = 0;
    for(;;)
    {
        if (udp->socket_ok)
        {
            ret = recvfrom(udp->socket_num, udp->sock_rx, DEMO_UDP_BUF_SIZE,
                           0, (struct sockaddr *)&pin, &addrlen);

            if (ret > 0)
            {
                printf("[udp]rcv from %s port:%d len=%d\n", inet_ntoa(pin.sin_addr), htons(pin.sin_port), ret);
                fifo_puts(&rcv_wlan_fifo, udp->sock_rx,ret);
            }
            else
            {
                printf("rcv err\n");
                udp->socket_ok = FALSE;
                closesocket(udp->socket_num);
                printf("closesocket\n");
                tls_os_queue_send(udp->udp_q, (void *)DEMO_MSG_SOCKET_ERR, 0);
            }
            continue;
        }
        tls_os_time_delay(10);
    }
}

int socket_udp_init(void)
{
    if (cn_udp)
    {
        printf("run\n");
        return WM_FAILED;
    }

    if (NULL == cn_udp)
    {
        cn_udp = tls_mem_alloc(sizeof(ST_Udp_Para));
        if (NULL == cn_udp)
        {
            goto _error;
        }
        memset(cn_udp, 0, sizeof(ST_Udp_Para));

        cn_udp->sock_rx = tls_mem_alloc(DEMO_UDP_BUF_SIZE + 1);
        if (NULL == cn_udp->sock_rx)
        {
            goto _error3;
        }

        cn_udp->sock_tx = tls_mem_alloc(DEMO_UDP_BUF_SIZE + 1);
        if (NULL == cn_udp->sock_tx)
        {
            goto _error4;
        }

        tls_os_queue_create(&(cn_udp->udp_q), DEMO_QUEUE_SIZE);
        //deal with socket's message
        tls_os_task_create(NULL, NULL,
                           demo_udp_task,
                           (void *)cn_udp,
                           (void *)udp_task_stk,          /* task's stack start address */
                           DEMO_UDP_TASK_SIZE*sizeof(u32),            /* task's stack size, unit:byte */
                           DEMO_UDP_TASK_PRIO,
                           0);

        //deal with socket's rx data
        tls_os_task_create(NULL, NULL,
                           demo_udp_recv_task,
                           (void *)cn_udp,
                           (void *)udp_rcv_task_stk,      /* task's stack start address */
                           DEMO_UDP_TASK_SIZE*sizeof(u32),            /* task's stack size, unit:byte */
                           DEMO_UDP_RECEIVE_TASK_PRIO,
                           0);
    }
    return WM_SUCCESS;

_error4:
    tls_mem_free(cn_udp->sock_rx);
_error3:
    tls_mem_free(cn_udp);
    cn_udp = NULL;
_error:
    return WM_FAILED;
}


static void udp_net_status_changed_event(u8 status )
{
    printf("[udp]udp_net_status_changed_event:%d",status);
    switch(status)
    {
    case NETIF_WIFI_JOIN_FAILED:
 //       tls_os_queue_send(cn_udp->udp_q, (void *)DEMO_MSG_WJOIN_FAILD, 0);
//         tls_os_queue_send(cn_udp->udp_q, (void *)DEMO_MSG_SOCKET_CREATE, 0);
        break;
    case NETIF_WIFI_JOIN_SUCCESS:
        tls_os_queue_send(cn_udp->udp_q, (void *)DEMO_MSG_WJOIN_SUCCESS, 0);
        break;
    case NETIF_IP_NET_UP:
        tls_os_queue_send(cn_udp->udp_q, (void *)DEMO_MSG_SOCKET_CREATE, 0);
        break;
    default:
        break;
    }
}

void udp_send_create(void)
{
    tls_os_queue_send(cn_udp->udp_q, (void *)DEMO_MSG_SOCKET_CREATE, 0);
}

void udp_send_del(void)
{
     tls_os_queue_send(cn_udp->udp_q, (void *)DEMO_MSG_WJOIN_FAILD, 0);
}

static void demo_udp_task(void *sdata)
{
    ST_Udp_Para *udp = (ST_Udp_Para *)sdata;
    void *msg;
    struct tls_ethif *ethif = tls_netif_get_ethif();
    int len;
    int ret;
    struct sockaddr_in pin;
    tls_netif_add_status_event(udp_net_status_changed_event);

    memset(&pin, 0, sizeof(struct sockaddr_in));
    pin.sin_family = AF_INET;              

    for (;;)
    {
        tls_os_queue_receive(udp->udp_q, (void **)&msg, 0, 0);
        //printf("\n msg =%d\n",msg);
        switch((u32)msg)
        {
        case DEMO_MSG_WJOIN_SUCCESS:
            break;

        case DEMO_MSG_SOCKET_CREATE:
            create_udp_socket_demo();
            break;

        case DEMO_MSG_WJOIN_FAILD:
            if(udp->socket_num > 0)
            {
                udp->socket_num = 0;
                udp->socket_ok = FALSE;
            }
            break;

        case DEMO_MSG_SOCKET_RECEIVE_DATA:
            break;

        case DEMO_MSG_UART_RECEIVE_DATA:
            break;

        case DEMO_MSG_SOCKET_ERR:
            tls_os_time_delay(500);
            create_udp_socket_demo( );
            break;

        default:
            break;
        }
    }

}


